# -*-coding:utf-8-*-

from tornado.gen import coroutine, Return
from delegates.base import BaseDelegate
from delegates.hfs.nds.products_delegate import NdsProduct
from delegates.hfs.nds.users_delegate import NdsUser
import time
import datetime
from daos.base_async import BaseRecord

SPECIFICS = {
    'vid': 'VENDOR_ID',
    'did': 'DEALER_ID',  # 经销商
    'sid': 'SPREADER_ID',  # 推广者
    'ssid': 'SUPERIOR_SPREADER_ID',  # 上级推广者
    'tsid': 'TOP_SPREADER_ID',  # 顶级推广者

    'spread_bonus_points': 100,
    'spread_total_price': 100,
    'bonus_points': 100,
    'total_price': 100,

    'commission_s': 15,
    'commission_ss': 9,
    'commission_ts': 6,
    'commission': 15,  # for spreader
}


class NdsOrder(BaseRecord):
    pass


class NdsOrdersDelegate(BaseDelegate):
    def __init__(self, *args, **kwargs):
        super(NdsOrdersDelegate, self).__init__(*args, **kwargs)
        self.return_expire_seconds = int(datetime.timedelta(days=7).total_seconds())
        self.commission_transfer_delay_seconds = int(datetime.timedelta(days=15).total_seconds())
        self.status_vendor = ['delivering', 'delivered', 'notify_return', 'refuse', 'returned']
        self.status_user = ['to_be_paid', 'paid', 'done', 'apply_return', 'returning']
        self.commission_s = 0.0
        self.commission_ss = 0.0
        self.commission_ts = 0.0

    @coroutine        
    def _on_pre_create_for_guest(self, **kwargs):
        user_specifics = yield self._get_user_specifics()
        user = NdsUser(user_specifics)
        specifics = {
            'total_price': kwargs['total_price'],
            'vid': user.vendor_id, # developer_id
            'uid': self._uid,
            'aid': self._app.id_str, # app_id
            'cur_status': 'to_be_paid',
            'is_guest': True,
        }
        # 假定扫码消费的 order & product 一对一的关系
        product = kwargs['products'][0]
        if 'specifics' in product:
            specifics.update(**product['specifics'])
        raise Return(specifics)

    @coroutine
    def _on_pre_create_for_commission(self, **kwargs):
        bonus_points = 0.0
        spread_bonus_points = 0.0
        spread_total_price = 0.0
        commission_s = 0.0
        commission_ss = 0.0
        commission_ts = 0.0
        
        user_specifics = yield self._get_user_specifics()
        user = NdsUser(user_specifics)

        self_role_names = yield self._get_user_role_names()
        self_role_names = set(self_role_names)
        if 'agent_city' in self_role_names:
            percentage_city = user.percentage_city
            percentage_dist = 0
            percentage_user = 0
            top_spreader_id = self._uid
        elif 'agent_district' in self_role_names:
            agent_city_specifics = yield self._get_user_specifics(user_id=user.superior_agent_id)
            agent_city = NdsUser(agent_city_specifics)
            percentage_city = agent_city.percentage_city
            percentage_dist = 0
            percentage_user = 0
            top_spreader_id = user.superior_agent_id
        else:
            agent_city_specifics = yield self._get_user_specifics(user_id=user.top_spreader_id)
            agent_city = NdsUser(agent_city_specifics)
            percentage_user = agent_city.percentage_user
            if user.superior_spreader_id is not None:
                agent_dist_specifics = yield self._get_user_specifics(user_id=user.superior_spreader_id)
                agent_dist = NdsUser(agent_dist_specifics)
                percentage_dist = agent_dist.percentage_dist
            else:
                percentage_dist = 0
            percentage_city = agent_city.percentage_city - percentage_dist - percentage_user
            top_spreader_id = user.top_spreader_id

        products = kwargs['products']
        for product in products:
            total_price = product['unit_price'] * product['quantity']
            product_specifics = yield self._get_product_specifics(product['product_id'])
            if not product_specifics:
                self._respond_error(1, 'invalid product')
            p = NdsProduct(product_specifics)
            if p.bonus_points_unit is not None and p.bonus_points_multiple is not None:
                bp = total_price / p.bonus_points_unit * p.bonus_points_multiple
                bonus_points += bp
                if p.increase_spread_bonus_points is not None and p.increase_spread_bonus_points:
                    spread_bonus_points += bp
                    spread_total_price += total_price
            commission_s += total_price * percentage_user / 100
            commission_ss += total_price * percentage_dist / 100
            commission_ts += total_price * percentage_city / 100
        specifics = {
            'commission_s': round(commission_s, 2),
            'commission_ss': round(commission_ss, 2),
            'commission_ts': round(commission_ts, 2),
            'bonus_points': bonus_points,
            'spread_bonus_points': spread_bonus_points,
            'spread_total_price': spread_total_price,
            'total_price': kwargs['total_price'],
            'vid': user.vendor_id,
            'sid': user.spreader_id,
            'ssid': user.superior_spreader_id,
            'tsid': top_spreader_id,
            'uid': self._uid,
            'cur_status': 'to_be_paid',
        }
        raise Return(specifics)
    
    @coroutine
    def on_pre_create(self, **kwargs):
        user_common_info = yield self._get_user_common_info()
        tags = user_common_info.get('tags', [])
        if 'guest' in tags:
            result = yield self._on_pre_create_for_guest(**kwargs)
        else:
            result = yield self._on_pre_create_for_commission(**kwargs)
        raise Return(result)
            
    def get_order_list_for_spread_aspect(self, **kwargs):
        uid = self._uid
        level = int(kwargs['level']) if 'level' in kwargs else 0
        if 0 == level:
            filter_cond = {
                '$or': [
                    {'specifics.sid': uid},
                    {'specifics.ssid': uid},
                    {'specifics.tsid': uid}
                ]}
        elif 1 == level:
            filter_cond = {'specifics.sid': uid}
        elif 2 == level:
            filter_cond = {'specifics.ssid': uid}
        elif 3 == level:
            filter_cond = {'specifics.tsid': uid}
        else:
            self._respond_error(1, 'invalid level: %d' % level)
        # 只有已完成的订单才参与提成计算
        filter_cond.update(status='done')
        return filter_cond, None

    def get_order_list(self, **kwargs):
        filter_cond = {
            '$or': [
                {'uid': self._uid},
                {'specifics.vid': self._uid},
            ]}
        # projection = {'uid': 0, 'vid': 0, 'did': 0}
        # projection.update(dict([(x, 0) for x in self.spread_fields]))
        if 'status' in kwargs:
            filter_cond.update(status=kwargs.pop('status'))
        if 'type' in kwargs:
            filter_cond.update(type=kwargs.pop('type'))
        return filter_cond, None

    def get_order_list_for_qrcode_pay_aspect(self, **kwargs):
        filter_cond = {
            'status': 'done',
            'specifics.vid': self._uid,
        }
        if 'device_id' in kwargs:
            filter_cond.update({'specifics.device_id': kwargs['device_id']})
        time_field_name = 'done_time'
        time_interval = []
        if 'start_time' in kwargs:
            time_interval.append({time_field_name: {'$gte': int(kwargs['start_time'])}})
        if 'end_time' in kwargs:
            time_interval.append({time_field_name: {'$lt': int(kwargs['end_time'])}})
        if 0 < len(time_interval):
            filter_cond.update({'$and': time_interval})
        return filter_cond, None
    
    def on_pre_get_list(self, **kwargs):
        aspect = kwargs.get('aspect', None)
        if 'spread' == aspect:
            result = self.get_order_list_for_spread_aspect(**kwargs)
        elif 'qrcode_pay' == aspect:
            result = self.get_order_list_for_qrcode_pay_aspect(**kwargs)
        else:
            result = self.get_order_list(**kwargs)
        raise Return(result)

    @coroutine
    def on_pre_traverse(self, **kwargs):
        self.commission_s = 0.0
        self.commission_ss = 0.0
        self.commission_ts = 0.0
        filter_cond = {
            '$or': [
                {'specifics.sid': self._uid},
                {'specifics.ssid': self._uid},
                {'specifics.tsid': self._uid}
            ],
            'status': 'done',
        }
        raise Return((filter_cond, None))

    def on_traversing_item(self, item, **kwargs):
        a = item['specifics']
        if a['sid'] == self._uid:
            if 'commission_s' in a:
                self.commission_s += a['commission_s']
        if a['ssid'] == self._uid:
            if 'commission_ss' in a:
                self.commission_ss += a['commission_ss']
        if a['tsid'] == self._uid:
            if 'commission_ts' in a:
                self.commission_ts += a['commission_ts']
        return True

    def on_post_traverse(self, **kwargs):
        return {
            'commission_as_s': round(self.commission_s, 2),
            'commission_as_ss': round(self.commission_ss, 2),
            'commission_as_ts': round(self.commission_ts, 2),
        }

    def on_pre_get(self, **kwargs):
        projection = {'_id': 0}
        filter_cond = {
            '$or': [
                {'uid': self._uid},
                {'specifics.vid': self._uid},
            ]}
        return filter_cond, projection

    def on_pre_get_statistics_for_commissions(self, **kwargs):
        match = {
            '$or': [
                {'specifics.sid': self._uid},
                {'specifics.ssid': self._uid},
                {'specifics.tsid': self._uid},
            ],
            'status': 'done',
        }
        time_field_name = 'done_time'
        if 'transfer_enable' in kwargs:
            match['transfer_enable'] = True  # kwargs['transfer_enable']
            time_field_name = 'transfer_enable_time'
        time_interval = []
        if 'start_time' in kwargs:
            time_interval.append({time_field_name: {'$gte': int(kwargs['start_time'])}})
        if 'end_time' in kwargs:
            time_interval.append({time_field_name: {'$lt': int(kwargs['end_time'])}})
        if 0 < len(time_interval):
            match['$and'] = time_interval
        pipeline = [
            {
                '$match': match,
            },
            {
                '$project': {
                    'commission_as_s': {
                        '$cond': [{'$eq': ['$specifics.sid', self._uid]}, '$specifics.commission_s', 0],
                    },
                    'commission_as_ss': {
                        '$cond': [{'$eq': ['$specifics.ssid', self._uid]}, '$specifics.commission_ss', 0],
                    },
                    'commission_as_ts': {
                        '$cond': [{'$eq': ['$specifics.tsid', self._uid]}, '$specifics.commission_ts', 0],
                    },
                }
            },
            {
                '$group': {
                    '_id': 0,
                    'commission_as_s': {'$sum': '$commission_as_s'},
                    'commission_as_ss': {'$sum': '$commission_as_ss'},
                    'commission_as_ts': {'$sum': '$commission_as_ts'},
                }
            },
        ]
        default = {
            'commission_as_s': 0,
            'commission_as_ss': 0,
            'commission_as_ts': 0,
        }
        return pipeline, default
    
    def on_pre_get_statistics_for_qrcode_pay(self, **kwargs):
        match = {
            'status': 'done',
            'specifics.vid': self._uid,
        }
        if 'device_id' in kwargs:
            match.update({'specifics.device_id': kwargs['device_id']})
        time_field_name = 'done_time'
        time_interval = []
        if 'start_time' in kwargs:
            time_interval.append({time_field_name: {'$gte': int(kwargs['start_time'])}})
        if 'end_time' in kwargs:
            time_interval.append({time_field_name: {'$lt': int(kwargs['end_time'])}})
        if 0 < len(time_interval):
            match['$and'] = time_interval
        pipeline = [
            {
                '$match': match,
            },
            {
                '$project': {
                    'income': '$specifics.total_price',
                }
            },
            {
                '$group': {
                    '_id': 0,
                    'total_income': {'$sum': '$income'},
                    'total_order_count': {'$sum': 1},
                }
            },
        ]
        default = {
            'total_income': 0,
            'total_order_count': 0,
        }
        return pipeline, default

    def on_pre_get_statistics(self, **kwargs):
        aspect = kwargs.get('aspect', None)
        if aspect is None or aspect not in ['commissions', 'qrcode_pay']:
            self._respond_error(1, 'unsupported aspect')
        if aspect == 'commissions':
            result = self.on_pre_get_statistics_for_commissions(**kwargs)
        elif aspect == 'qrcode_pay':
            result = self.on_pre_get_statistics_for_qrcode_pay(**kwargs)
        return result

    @coroutine
    def _on_update_order_status(self, order, status):
        # vendor 和 normal user 可以修改的状态不一样
        if self._uid == order.vid:
            if status not in self.status_vendor:
                self._respond_error(1, 'invalid status(%s)for vendor, cur_status: %s ' % (status, order.cur_status))
        elif self._uid == order.uid:
            if status not in self.status_user:
                self._respond_error(2, 'invalid status(%s)for user, cur_status: %s ' % (status, order.cur_status))
        else:
            self._respond_error(3, 'invalid user')
        # 后续业务需要,将新旧两状态存于 specifics 中
        modification = {'cur_status': status, 'old_status': order.cur_status}
        raise Return(modification)

    @coroutine
    def on_pre_update(self, **kwargs):
        oid = kwargs['oid']
        modification = kwargs['modification']

        order_specifics = yield self._get_order_specifics(oid)
        order = NdsOrder(order_specifics)

        if 'status' in modification:
            status = modification['status']
            modification = yield self._on_update_order_status(order, status)
            order_specifics.update(modification)

        raise Return(order_specifics)

    @coroutine
    def _update_consumptions_and_commissions(self, order):
        consumptions = {
            'consumptions.bonus_points': order.bonus_points,
            'consumptions.spread_bonus_points': order.spread_bonus_points,
            'consumptions.amount': order.total_price,
            'consumptions.spread_amount': order.spread_total_price,
        }
        yield self._update_user_specifics(self._uid, {'$inc': consumptions})
        if order.sid is not None:
            yield self._update_user_specifics(order.sid, {'$inc': {'commissions.as_spreader': order.commission_s}})
        if order.ssid is not None:
            yield self._update_user_specifics(order.ssid,
                                              {'$inc': {'commissions.as_superior_spreader': order.commission_ss}})
        if order.tsid is not None:
            yield self._update_user_specifics(order.tsid,
                                              {'$inc': {'commissions.as_top_spreader': order.commission_ts}})

    @coroutine
    def on_post_update(self, **kwargs):
        oid = kwargs['oid']

        order_specifics = yield self._get_order_specifics(oid)
        order = NdsOrder(order_specifics)

        if order.cur_status == order.old_status:
            raise Return()

        if 'done' == order.cur_status:
            if not order.is_guest:
                yield self._update_consumptions_and_commissions(order)
